#ifndef FFPLAY_DEF_H
#define FFPLAY_DEF_H

#include <inttypes.h>
#include <math.h>
#include <limits.h>
#include <signal.h>
#include <stdint.h>

extern "C"{
#include "libavutil/avstring.h"
#include "libavutil/eval.h"
#include "libavutil/mathematics.h"
#include "libavutil/pixdesc.h"
#include "libavutil/imgutils.h"
#include "libavutil/dict.h"
#include "libavutil/parseutils.h"
#include "libavutil/samplefmt.h"
#include "libavutil/avassert.h"
#include "libavutil/time.h"
#include "libavformat/avformat.h"
#include "libavdevice/avdevice.h"
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
#include "libavcodec/avfft.h"
#include "libswresample/swresample.h"
#include <libavcodec/avcodec.h>
#include "SDL.h"
}

#include <SDL.h>
#include <SDL_thread.h>

#include <assert.h>

#define MAX_QUEUE_SIZE (15 * 1024 * 1024)
#define MIN_FRAMES 25
#define EXTERNAL_CLOCK_MIN_FRAMES 2
#define EXTERNAL_CLOCK_MAX_FRAMES 10

#define SDL_VOLUME_STEP (0.75)

#define AV_SYNC_THRESHOLD_MIN 0.04
#define AV_SYNC_THRESHOLD_MAX 0.1
#define AV_SYNC_FRAMEDUP_THRESHOLD 0.1
#define AV_NOSYNC_THRESHOLD 10.0

/*
 * 用于采样并记录速度相关信息的结构体
 * 该结构体可用于记录速度采样的范围、上一次采样的时间点、持续时间、采样量以及速度等信息。
*/
typedef struct SDL_SpeedSampler2
{
    //采样范围,决定了在计算速度时考虑多长时间内的数据
    int64_t sample_range;

    //上一次采样的时间点,记录上一次进行速度采样时的时间戳,用于计算两次采样之间的时间间隔。
    int64_t last_profile_tick;

    //上一次采样的持续时间,记录上一次速度采样过程所持续的时间,用于计算速度。
    int64_t last_profile_duration;

    //上一次采样的数量,例如传输的字节数、处理的帧数等
    int64_t last_profile_quantity;

    //上一次采样计算得到的速度
    int64_t last_profile_speed;
} SDL_SpeedSampler2;

//用于存储音视频轨道缓存的统计信息
typedef struct FFTrackCacheStatistic
{
    //缓存数据的总时长,以毫秒为单位代表当前缓存的音视频数据总共可以播放的时长。
    int64_t duration;

    //缓存数据的总字节数,以毫秒为单位,代表当前缓存的音视频数据在存储空间中占用的字节数量。
    int64_t bytes;

    //缓存的数据包数量,记录当前缓存中包含的音视频数据包的个数。
    int64_t packets;
} FFTrackCacheStatistic;

//用于存储音视频播放过程中的各种统计信息
typedef struct FFStatistic
{
    //视频解码器类型
    int64_t vdec_type;
    //视频帧率
    float vfps;
    //视频解码速度,单位为帧/秒（fps）
    float vdps;
    //音视频延迟,音频和视频之间的时间差，单位为秒，用于衡量音视频同步的程度。
    float avdelay;
    //音视频差异,进一步描述音频和视频在时间上的差异程度，单位为秒。
    float avdiff;
    //视频流码率
    int64_t bit_rate;

    //视频缓存统计信息
    FFTrackCacheStatistic video_cache;
    //音频缓存统计信息
    FFTrackCacheStatistic audio_cache;

    //缓冲区向后位置
    int64_t buf_backwards;
    //缓冲区向前位置
    int64_t buf_forwards;
    //缓冲区总容量
    int64_t buf_capacity;

    //读取速度采样器
    SDL_SpeedSampler2 tcp_read_sampler;

    //最近一次 seek 操作后的加载时长
    int64_t latest_seek_load_duration;

    //已读取的字节总数
    int64_t byte_count;

    //缓存文件的物理位置
    int64_t cache_physical_pos;
    //缓存文件向前位置
    int64_t cache_file_forwards;
    //缓存文件的当前位置
    int64_t cache_file_pos;
    //缓存文件中的字节数
    int64_t cache_count_bytes;
    //逻辑文件大小,文件的理论大小
    int64_t logical_file_size;

    //丢帧数量
    int drop_frame_count;

    //解码的视频帧数 记录已经成功解码的视频帧数。
    int decode_frame_count;

    //丢帧率
    float drop_frame_rate;
} FFStatistic;

//用于表示函数执行结果或操作状态。
enum RET_CODE
{
    RET_ERR_UNKNOWN = -2,                   // 未知错误
    RET_FAIL = -1,							// 失败
    RET_OK	= 0,							// 正常
    RET_ERR_OPEN_FILE,						// 打开文件失败
    RET_ERR_NOT_SUPPORT,					// 不支持
    RET_ERR_OUTOFMEMORY,					// 没有内存
    RET_ERR_STACKOVERFLOW,					// 溢出
    RET_ERR_NULLREFERENCE,					// 空参考
    RET_ERR_ARGUMENTOUTOFRANGE,				//参数超出有效范围
    RET_ERR_PARAMISMATCH,					// 参数不匹配
    RET_ERR_MISMATCH_CODE,                  // 没有匹配的编解码器
    RET_ERR_EAGAIN,                         // 资源暂时不可用，需要稍后重试，通常用于 I/O 操作或资源竞争场景
    RET_ERR_EOF                             // 到达文件末尾，在读取文件时已到达文件的最后位置
};

typedef struct MyAVPacketList {
    AVPacket		pkt;    //解封装后的数据
    struct MyAVPacketList	*next;  //下一个节点
    int			serial;     //播放序列
} MyAVPacketList;

/*
表示一个数据包队列
用来缓存解封装后的音视频数据包，保证数据能有序、高效地被读取和处理
*/
typedef struct PacketQueue {
    MyAVPacketList	*first_pkt, *last_pkt;  // 队首，队尾指针
    int		nb_packets;   // 包数量，也就是队列元素数量
    int		size;         // 队列所有元素的数据大小总和
    int64_t		duration; // 队列所有元素的数据播放持续时间
    int		abort_request; // 用户退出请求标志
    int		serial;         // 播放序列号，和MyAVPacketList的serial作用相同，但改变的时序稍微有点不同
    SDL_mutex	*mutex;     // 用于维持PacketQueue的多线程安全(SDL_mutex可以按pthread_mutex_t理解）
    SDL_cond	*cond;      // 用于读、写线程相互通知(SDL_cond可以按pthread_cond_t理解)
} PacketQueue;

#define VIDEO_PICTURE_QUEUE_SIZE	3       // 图像帧缓存数量
#define VIDEO_PICTURE_QUEUE_SIZE_MIN        (3)
#define VIDEO_PICTURE_QUEUE_SIZE_MAX        (16)
#define VIDEO_PICTURE_QUEUE_SIZE_DEFAULT    (VIDEO_PICTURE_QUEUE_SIZE_MIN)
#define SUBPICTURE_QUEUE_SIZE		16      // 字幕帧缓存数量
#define SAMPLE_QUEUE_SIZE           9       // 采样帧缓存数量
#define FRAME_QUEUE_SIZE FFMAX(SAMPLE_QUEUE_SIZE, FFMAX(VIDEO_PICTURE_QUEUE_SIZE, SUBPICTURE_QUEUE_SIZE))


typedef struct AudioParams {
    int			freq;                   // 采样率
    int			channels;               // 通道数
    int64_t		channel_layout;         // 通道布局，比如2.1声道，5.1声道等
    enum AVSampleFormat	fmt;            // 音频采样格式，比如AV_SAMPLE_FMT_S16表示为有符号16bit深度，交错排列模式。
    int			frame_size;             // 一个采样单元占用的字节数（比如2通道时，则左右通道各采样一次合成一个采样单元）
    int			bytes_per_sec;          // 一秒时间的字节数，比如采样率48Khz，2 channel，16bit，则一秒48000*2*16/8=192000
} AudioParams;

/* Common struct for handling all types of decoded data and allocated render buffers. */
// 用于缓存解码后的音视频帧信息。
typedef struct Frame {
    AVFrame		*frame;         // 指向数据帧
    int		serial;             // 帧序列，在seek的操作时serial会变化,用于标识不同的播放序列
    double		pts;            // 时间戳，单位为秒
    double		duration;       // 该帧持续时间，单位为秒
    int64_t pos;                // 帧在输入流中的字节位置，可用于定位和查找帧数据
    int		width;              // 图像宽度
    int		height;             // 图像高读
    int		format;             //  数据格式,对于视频帧，该值为 enum AVPixelFormat 类型，表示像素格式
    AVRational sar;              // 采样宽高比（Sample Aspect Ratio），用于修正图像的显示比例
    int uploaded;               // 标记该帧是否已经上传到渲染设备（如 GPU），1 表示已上传，0 表示未上传
    int flip_v;                  // 垂直翻转标志，1 表示需要将图像垂直翻转后再显示，0 表示不翻转
} Frame;


/* 这是一个循环队列，windex是指其中的首元素，rindex是指其中的尾部元素. */
typedef struct FrameQueue {
    Frame	queue[FRAME_QUEUE_SIZE];        // FRAME_QUEUE_SIZE  最大size, 数字太大时会占用大量的内存，需要注意该值的设置
    int		rindex;                         // 读索引。待播放时读取此帧进行播放，播放后此帧成为上一帧
    int		windex;                         // 写索引
    int		size;                           // 当前总帧数
    int		max_size;                       // 可存储最大帧数
    int keep_last;
    int rindex_shown;
    SDL_mutex	*mutex;                     // 互斥量
    SDL_cond	*cond;                      // 条件变量
    PacketQueue	*pktq;                      // 数据包缓冲队列
} FrameQueue;

// 这里讲的系统时钟 是通过av_gettime_relative()获取到的时钟，单位为微妙
typedef struct Clock {
    double	pts;            // 时钟基础, 当前帧(待播放)显示时间戳，播放后，当前帧变成上一帧
    // 当前pts与当前系统时钟的差值, audio、video对于该值是独立的
    double	pts_drift;      // clock base minus time at which we updated the clock
    // 当前时钟(如视频时钟)最后一次更新时间，也可称当前时钟时间
    double	last_updated;   // 最后一次更新的系统时钟
    double	speed;          // 时钟速度控制，用于控制播放速度
    // 播放序列，所谓播放序列就是一段连续的播放动作，一个seek操作会启动一段新的播放序列
    int	serial;             // clock is based on a packet with this serial
    int	paused;             // = 1 说明是暂停状态
    // 指向packet_serial
    int *queue_serial;      /* pointer to the current packet queue serial, used for obsolete clock detection */
} Clock;

/**
 *音视频同步方式，缺省以音频为基准
 */
enum {
    AV_SYNC_UNKNOW_MASTER = -1,
    AV_SYNC_AUDIO_MASTER,                   // 以音频为基准
    AV_SYNC_VIDEO_MASTER,                   // 以视频为基准
//    AV_SYNC_EXTERNAL_CLOCK,                 // 以外部时钟为基准，synchronize to an external clock */
};

//将时间戳转换为毫秒
#define fftime_to_milliseconds(ts) (av_rescale(ts, 1000, AV_TIME_BASE))
//将毫秒转换为时间戳
#define milliseconds_to_fftime(ms) (av_rescale(ms, AV_TIME_BASE, 1000))

extern  AVPacket flush_pkt;
// 队列相关
int packet_queue_put(PacketQueue *q, AVPacket *pkt);
int packet_queue_put_nullpacket(PacketQueue *q, int stream_index);
int packet_queue_init(PacketQueue *q);
void packet_queue_flush(PacketQueue *q);
void packet_queue_destroy(PacketQueue *q);
void packet_queue_abort(PacketQueue *q);
void packet_queue_start(PacketQueue *q);
int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block, int *serial);


/**
 * @brief 获取帧缓存的数据可以播放的时间长度
 * @param q 队列本身
 * @param time_base 用于计算packet的时间戳转换
 * @param packet_duration 单个包可以播放的时长
 * @return 返回时长以秒为单位
 */
double packet_queue_cache_duration(PacketQueue *q, AVRational time_base, double packet_duration);

/* 初始化FrameQueue，视频和音频keep_last设置为1，字幕设置为0 */
int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last);
void frame_queue_destory(FrameQueue *f);
void frame_queue_signal(FrameQueue *f);
/* 获取队列当前Frame, 在调用该函数前先调用frame_queue_nb_remaining确保有frame可读 */
Frame *frame_queue_peek(FrameQueue *f);

/* 获取当前Frame的下一Frame, 此时要确保queue里面至少有2个Frame */
// 不管你什么时候调用，返回来肯定不是 NULL
Frame *frame_queue_peek_next(FrameQueue *f);
/* 获取last Frame：
 */
Frame *frame_queue_peek_last(FrameQueue *f);
// 获取可写指针
Frame *frame_queue_peek_writable(FrameQueue *f);
// 获取可读
Frame *frame_queue_peek_readable(FrameQueue *f);
// 更新写指针
void frame_queue_push(FrameQueue *f);
/* 释放当前frame，并更新读索引rindex */
void frame_queue_next(FrameQueue *f);
int frame_queue_nb_remaining(FrameQueue *f);
int64_t frame_queue_last_pos(FrameQueue *f);

// 时钟相关
double get_clock(Clock *c);
void set_clock_at(Clock *c, double pts, int serial, double time);
void set_clock(Clock *c, double pts, int serial);
void init_clock(Clock *c, int *queue_serial);

 void ffp_reset_statistic(FFStatistic *dcc);

#endif // FFPLAY_DEF_H
